package net.minecraft.util;

import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.UnmodifiableIterator;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

public class Cartesian
{
    private static final String __OBFID = "CL_00002327";

    /**
     * Create the cartesian product. This method returns an Iterable of arrays of type clazz.
     *  
     * @param sets the Sets to combine. This is an Iterable of Iterables of type clazz
     */
    public static Iterable cartesianProduct(Class clazz, Iterable sets)
    {
        return new Cartesian.Product(clazz, (Iterable[])toArray(Iterable.class, sets), null);
    }

    /**
     * Like cartesianProduct(Class, Iterable) but returns an Iterable of Lists instead.
     */
    public static Iterable cartesianProduct(Iterable sets)
    {
        return arraysAsLists(cartesianProduct(Object.class, sets));
    }

    /**
     * Convert an Iterable of Arrays (Object[]) to an Iterable of Lists
     */
    private static Iterable arraysAsLists(Iterable arrays)
    {
        return Iterables.transform(arrays, new Cartesian.GetList(null));
    }

    /**
     * Create a new Array of type clazz with the contents of the given Iterable
     */
    private static Object[] toArray(Class clazz, Iterable it)
    {
        ArrayList var2 = Lists.newArrayList();
        Iterator var3 = it.iterator();

        while (var3.hasNext())
        {
            Object var4 = var3.next();
            var2.add(var4);
        }

        return (Object[])var2.toArray(createArray(clazz, var2.size()));
    }

    private static Object[] createArray(Class p_179319_0_, int p_179319_1_)
    {
        return (Object[])((Object[])Array.newInstance(p_179319_0_, p_179319_1_));
    }

    static class GetList implements Function
    {
        private static final String __OBFID = "CL_00002325";

        private GetList() {}

        public List apply(Object[] array)
        {
            return Arrays.asList((Object[])array);
        }

        public Object apply(Object p_apply_1_)
        {
            return this.apply((Object[])p_apply_1_);
        }

        GetList(Object p_i46022_1_)
        {
            this();
        }
    }

    static class Product implements Iterable
    {
        private final Class clazz;
        private final Iterable[] iterables;
        private static final String __OBFID = "CL_00002324";

        private Product(Class clazz, Iterable[] iterables)
        {
            this.clazz = clazz;
            this.iterables = iterables;
        }

        public Iterator iterator()
        {
            return (Iterator)(this.iterables.length <= 0 ? Collections.singletonList((Object[])Cartesian.createArray(this.clazz, 0)).iterator() : new Cartesian.Product.ProductIterator(this.clazz, this.iterables, null));
        }

        Product(Class p_i46021_1_, Iterable[] p_i46021_2_, Object p_i46021_3_)
        {
            this(p_i46021_1_, p_i46021_2_);
        }

        static class ProductIterator extends UnmodifiableIterator
        {
            private int index;
            private final Iterable[] iterables;
            private final Iterator[] iterators;
            private final Object[] results;
            private static final String __OBFID = "CL_00002323";

            private ProductIterator(Class clazz, Iterable[] iterables)
            {
                this.index = -2;
                this.iterables = iterables;
                this.iterators = (Iterator[])Cartesian.createArray(Iterator.class, this.iterables.length);

                for (int var3 = 0; var3 < this.iterables.length; ++var3)
                {
                    this.iterators[var3] = iterables[var3].iterator();
                }

                this.results = Cartesian.createArray(clazz, this.iterators.length);
            }

            private void endOfData()
            {
                this.index = -1;
                Arrays.fill(this.iterators, (Object)null);
                Arrays.fill(this.results, (Object)null);
            }

            public boolean hasNext()
            {
                if (this.index == -2)
                {
                    this.index = 0;
                    Iterator[] var5 = this.iterators;
                    int var2 = var5.length;

                    for (int var3 = 0; var3 < var2; ++var3)
                    {
                        Iterator var4 = var5[var3];

                        if (!var4.hasNext())
                        {
                            this.endOfData();
                            break;
                        }
                    }

                    return true;
                }
                else
                {
                    if (this.index >= this.iterators.length)
                    {
                        for (this.index = this.iterators.length - 1; this.index >= 0; --this.index)
                        {
                            Iterator var1 = this.iterators[this.index];

                            if (var1.hasNext())
                            {
                                break;
                            }

                            if (this.index == 0)
                            {
                                this.endOfData();
                                break;
                            }

                            var1 = this.iterables[this.index].iterator();
                            this.iterators[this.index] = var1;

                            if (!var1.hasNext())
                            {
                                this.endOfData();
                                break;
                            }
                        }
                    }

                    return this.index >= 0;
                }
            }

            public Object[] next0()
            {
                if (!this.hasNext())
                {
                    throw new NoSuchElementException();
                }
                else
                {
                    while (this.index < this.iterators.length)
                    {
                        this.results[this.index] = this.iterators[this.index].next();
                        ++this.index;
                    }

                    return (Object[])this.results.clone();
                }
            }

            public Object next()
            {
                return this.next0();
            }

            ProductIterator(Class p_i46019_1_, Iterable[] p_i46019_2_, Object p_i46019_3_)
            {
                this(p_i46019_1_, p_i46019_2_);
            }
        }
    }
}
